#include "Quimera_protocol_v1.h"
#include <stdlib.h>
#include <sys/msg.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sched.h>
#include <dlfcn.h>
#include <sys/shm.h>


unsigned int Quimera_server::ids_pointer=0;
bool Quimera_utils::interrupt=false;
bool Quimera_server::quality_control=false;
bool Quimera_server::interrupt=false;
bool Quimera_client::online=false;
bool Quimera_client::interrupt=false;
bool Quimera_client::quality_control=false;

std::map<unsigned int,bool> Quimera_client::iclist = create_map<unsigned int, bool > (0,true);
std::map<unsigned int,bool>::iterator Quimera_client::tic=iclist.end();


inline void* Quimera_utils::Background_t(void *t){
    
    std::cout<<"En el hilo\n";
    Quimera_utils *qu=(Quimera_utils*)t;
    usleep(U_SECONDS*qu->t_wait);
    qu->locker=false;
    std::cout<<"Mori\n";
    pthread_exit(NULL);
    
} 

inline int Quimera_utils::Send(int &sock, struct msghdr &mhdr,  struct iovec *vector, Envlp &envlp){
   
     vector[0].iov_base = envlp.buff;
     vector[0].iov_len = envlp.length;
     mhdr.msg_iov = vector;
     return(sendmsg(sock, &mhdr, 0));
     
  
}

inline int Quimera_utils::SendRecv(int &uqid,const long &mid, unsigned short time, unsigned int &n_register, Package &buff){
    
    bzero((char *) &buff, sizeof(buff));
    bzero((char *) &msg_queue, sizeof(msg_queue));
    
    locker=true;
    t_wait=time;
    
    std::cout<<"El timepo fue de:" <<int(time)<<std::endl;
    pthread_create(&init, NULL,&Quimera_utils::Background_t, this);
    std::cout<<"El numero de registro enviado:"<<n_register<<std::endl;
    std::cout<<"La llame en sendrecv:"<<uqid<<std::endl;
   
    while(locker){
        if(msgrcv(uqid,(struct msgbuf *)&msg_queue,sizeof(msg_queue)-sizeof(long),mid,IPC_NOWAIT)>0){     
            buff=msg_queue.package; 
            std::cout<<"El numero de registro recibido:"<<msg_queue.package.header.n_register<<std::endl;
            std::cout<<"El id del servidor es:"<<msg_queue.package.header.id_server_source<<std::endl;
            if(buff.header.n_register==n_register){
                pthread_cancel(init);
                locker=false;
                return (EXIT_SUCCESS);
            }
            else{
                
                std::cout<<"Fue el register\n:";
                pthread_cancel(init);
                locker=false;
                return (EXIT_FAILURE);
            }
        }
        usleep(DEFAULT_WAIT);
    
    }
    pthread_join(init,NULL);
    std::cout<<"Nunca llego\n";
    return (EXIT_FAILURE);   
}

inline Quimera_server::Quimera_server(unsigned char &dom, Q_pool &qpool, unsigned short &ftime, Q_density &q_d) {
    
    bzero((char *) &mh, sizeof(mh));
    bzero((char *) &v, sizeof(v));
    
    domain=dom;
    pool=qpool;
    f_time=ftime;
    q_dent.cycle=q_d.cycle;
    q_dent.switchpop=q_d.switchpop;
    ids_pointer=0;
    quimera_id=pool.first;
    mutex = PTHREAD_MUTEX_INITIALIZER;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    p_this=this;
    interrupt=false;

    
    
    
    handle = dlopen("libframe.so", RTLD_LAZY);
    create = (Message* (*)())dlsym(handle, "create_t");
    destroy = (void (*)(Message*))dlsym(handle, "destroy_t");
    MSG = (Message*)create();
    qpkg=&MSG->pkg;
    
    
    
    upkr_queue_id=Message_queue_setup(S_UNPACKER_QUEUE_KEY);
    l_queue_id=Message_queue_setup(LOGGER_QUEUE_KEY);
    
    if(upkr_queue_id==EXIT_FAILURE and l_queue_id!=EXIT_FAILURE){
        
        bzero((char *) &log_msg, sizeof(log_msg));
        log_msg.id_message=LOG_QUEUE_ERR_UNPACKER_QSP;
        msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
        sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
        quality_control=false;
    }
    else
        quality_control=true;
}

inline Quimera_server::~Quimera_server(){
    close(sockfd);
    destroy(MSG);
    pthread_mutex_destroy(&mutex);
}


inline long Quimera_server::C_hash(std::string &seed, const std::collate<char>& coll){
    
    return (coll.hash(seed.data(),seed.data()+seed.length()));
}

inline void *Quimera_server::Serial_killer(void *t){
    
    Quimera_server *qs=(Quimera_server*)t;
    long next_victim=0;
    int diff=0;
    int ref=0;
    time_t now;
    char ip_mid[16];
    
    while(!qs->interrupt){
     
        if(qs->clist.begin()!=qs->clist.end()){
            
            pthread_mutex_lock(&qs->mutex);
            for(qs->ti=qs->clist.begin();qs->ti!=qs->clist.end();qs->ti++){
                time(&now);
                diff=difftime((*qs->ti).second.check_in,now);
                
                if(diff >= ref){
                    ref=diff;
                    next_victim=qs->ti->first;
                }     
            }
        pthread_mutex_unlock(&qs->mutex);
        
        if(diff>0)
            sleep(diff);
        
        pthread_mutex_lock(&qs->mutex);
        qs->ti=qs->clist.find(next_victim);
        time(&now);
        
        if(difftime((*qs->ti).second.check_in,now)<=0){
            
            bzero((char *) &qs->log_msg, sizeof(qs->log_msg));
            qs->log_msg.id_message=LOG_QUEUE_EVNT_CLOGOUT;
            strcpy(ip_mid,(*qs->ti).second.ip);
            qs->log_msg.cli_addr.sin_addr.s_addr=inet_addr((*qs->ti).second.ip);
            qs->log_msg.package.header.n_register=(*qs->ti).second.n_register;
            qs->log_msg.package.header.id_server_destination=(*qs->ti).second.q_id;
            msgsnd(qs->l_queue_id, (struct msgbuf *)&qs->log_msg,
            sizeof(qs->log_msg)-sizeof(long),IPC_NOWAIT);
            qs->clist.erase(next_victim);
        }
        pthread_mutex_unlock(&qs->mutex);
        
    }
    
    else
        sleep(qs->f_time);
    
    }
}

inline void *Quimera_server::Complaint_killer(void *t){
    
    Quimera_server *qs=(Quimera_server*)t;
    long next_victim=0;
    int diff=0;
    int ref=0;
    time_t now;
    
    while(!qs->interrupt){
     
        if(qs->mglist.begin()!=qs->mglist.end()){
            
            pthread_mutex_lock(&qs->mutex);
            for(qs->mgit=qs->mglist.begin();qs->mgit!=qs->mglist.end();qs->mgit++){
                time(&now);
                diff=difftime((*qs->mgit).second.checkin_time,now);
                
                if(diff >= ref){
                    ref=diff;
                    next_victim=qs->mgit->first;
                }     
            }
        pthread_mutex_unlock(&qs->mutex);
        
        if(diff>0)
            sleep(diff);
        
        pthread_mutex_lock(&qs->mutex);
        qs->mgit=qs->mglist.find(next_victim);
        time(&now);
        
        if(difftime((*qs->mgit).second.checkin_time,now)<=0){
            std::cout<<"El tiempo ha sido de:"<<(*qs->mgit).second.checkin_time<<std::endl;
            /*bzero((char *) &qs->log_msg, sizeof(qs->log_msg));
            qs->log_msg.id_message=LOG_QUEUE_EVNT_EXPIRE;
            qs->log_msg.package=(*qs->mgit).second.message.package;
            qs->log_msg.cli_addr=(*qs->mgit).second.message.cli_addr;
            msgsnd(qs->l_queue_id, (struct msgbuf *)&qs->log_msg,
            sizeof(qs->log_msg)-sizeof(long),IPC_NOWAIT);*/
            qs->mglist.erase(next_victim);
        }
        pthread_mutex_unlock(&qs->mutex);
        
    }
    
    else
        sleep(qs->f_time);
    
    }
}

inline unsigned int Quimera_server::Give_me_ID(sockaddr_in& id_client, unsigned int &n_register){
    
    if((pool.first+ids_pointer)>=pool.last){
            
        bzero((char *) &log_msg, sizeof(log_msg));
        log_msg.id_message=LOG_QUEUE_ERR_TIMEOUT;
        msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
        sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
        return(EXIT_FAILURE);
    }
    else{
         
        h_seed=inet_ntoa(id_client.sin_addr);
        clhash = C_hash(h_seed,std::use_facet<std::collate<char> >(loc));
        ti=clist.find(this->clhash);
        if(ti!=clist.end())
            return((*ti).second.q_id);        
         
        else{ 
            bzero((char *) &cmap, sizeof(cmap));   
            
            ids_pointer=ids_pointer+1;
        
            cmap.q_id=pool.first+ids_pointer;
            cmap.n_register=n_register;
            time(&cmap.check_in);
            strcpy(cmap.ip,h_seed.c_str());
          
            ti=clist.begin();
            clist.insert(std::pair<long,cli_map>(clhash,cmap));
        
            bzero((char *) &log_msg, sizeof(log_msg));
            log_msg.id_message=LOG_QUEUE_EVNT_CREGISTER;
            log_msg.cli_addr=id_client;
            log_msg.package.header.n_register=cmap.n_register;
            log_msg.package.header.id_server_destination=cmap.q_id;
            msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
            sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
            return(pool.first+ids_pointer);
        }
    }
}


inline int Quimera_server::Listen(){
   
 
    if(pthread_create(&init,NULL,&Quimera_server::Serial_killer,this)!=0){
        
        bzero((char *) &log_msg, sizeof(log_msg));
        log_msg.id_message=LOG_QUEUE_ERR_SKILLER;
        msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
        sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
        
        interrupt=true;
    }
    
    if(pthread_create(&init_2,NULL,&Quimera_server::Complaint_killer,this)!=0){
        
        bzero((char *) &log_msg, sizeof(log_msg));
        log_msg.id_message=LOG_QUEUE_ERR_CKILLER;
        msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
        sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
        
        interrupt=true;
    }
    
    while(!interrupt){
       
        bzero((char *) &buff, sizeof(buff));
        msgrcv(upkr_queue_id,(struct msgbuf *)&buff,sizeof(buff)-sizeof(long),0,0);
        pthread_mutex_lock(&mutex);
        
        switch(buff.package.header.type_message){
       
            case MEMBERSHIP:{
                Membership_procedure(buff);
                break;
                
            }
            case INVESTMENT_CONFIRMATION:{
                Investment_procedure(buff);
                break;
            }
            case GUARD_REPORT:{
                Complaint_procedure(buff);
                break;
            }
            case MISSING_REPORT:{
                Complaint_procedure(buff);
                break;
            }
                
        }
        if(buff.id_message==CLR)
        { 
         interrupt=true;
        }
        pthread_mutex_unlock(&mutex); 
         
    }
     msgctl (upkr_queue_id, IPC_RMID, (struct msqid_ds *)NULL);
    return(EXIT_SUCCESS);
}

inline void Quimera_server::Membership_procedure(Message_queue request){
    

    bzero((char *) &buff, sizeof(buff));
    buff.package.header.id_server_destination=Give_me_ID(request.cli_addr,request.package.header.n_register);
    
    if(buff.package.header.id_server_destination!=EXIT_FAILURE){
        
        MSG->Fill_header(domain,quimera_id,buff.package.header.id_server_destination);
        MSG->Investment(f_time);
        MSG->pkg.header.n_register=request.package.header.n_register;
        MSG->pkg.header.id_server_source=quimera_id;
        buff.package=MSG->pkg;
        buff.package.header.id_server_source=quimera_id;
        buff.cli_addr=request.cli_addr;
        buff.cli_addr.sin_port=htons(QUIMERA_PORT);
        bzero((char *) &mh, sizeof(mh));
        bzero((char *) &v, sizeof(v));
        mh.msg_name = &buff.cli_addr;
        mh.msg_namelen = sizeof(buff.cli_addr);
        mh.msg_iovlen = 1;
        
        bzero((char *) &envelope, sizeof(envelope));
        qpkg=&buff.package;
        envelope.buff=(void*)qpkg;
        envelope.length=sizeof(*qpkg);
         
        if(Send(sockfd,mh,v,envelope)>0){
            bzero((char *) &log_msg, sizeof(log_msg));
            log_msg.id_message=LOG_QUEUE_EVNT_OUT_INVESTMENT;
            log_msg.cli_addr=request.cli_addr;
            log_msg.package=MSG->pkg;
            msgsnd(l_queue_id,(struct msgbuf *)&log_msg,
            sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
        }
        
        else{
            bzero((char *) &log_msg, sizeof(log_msg));
            log_msg.id_message=LOG_QUEUE_ERR_OUT_SEND;
            log_msg.cli_addr=request.cli_addr;
            log_msg.package=MSG->pkg;
            msgsnd(l_queue_id, (struct msgbuf *)&this->log_msg,
            sizeof(log_msg)-sizeof(long),IPC_NOWAIT);    
        }
    }
        
}

inline void Quimera_server::Investment_procedure(Message_queue request){
     

    bzero((char *) &buff, sizeof(buff));
    
    buff.cli_addr=request.cli_addr;
    buff.cli_addr.sin_port=htons(QUIMERA_PORT);
    buff.package=request.package;
    buff.package.header.id_server_source=request.package.header.id_server_destination;
    buff.package.header.id_server_destination=request.package.header.id_server_source;
    buff.package.header.n_register=request.package.header.n_register;
    bzero((char *) &mh, sizeof(mh));
    bzero((char *) &v, sizeof(v));
    mh.msg_name = &buff.cli_addr;
    mh.msg_namelen = sizeof(buff.cli_addr);
    mh.msg_iovlen = 1;
        
    bzero((char *) &envelope, sizeof(envelope));
    qpkg=&buff.package;
    envelope.buff=(void*)qpkg;
    envelope.length=sizeof(*qpkg);
    
    if(Send(sockfd,mh,v,envelope)>0){
            
            bzero((char *) &log_msg, sizeof(log_msg));
            log_msg.id_message=LOG_QUEUE_EVNT_OUT_INVESTMENT_C;
            log_msg.cli_addr=request.cli_addr;
            log_msg.package=MSG->pkg;
            msgsnd(l_queue_id,(struct msgbuf *)&log_msg,
            sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
            
            h_seed=inet_ntoa(buff.cli_addr.sin_addr);
            clhash = C_hash(h_seed,std::use_facet<std::collate<char> >(this->loc));
            ti=clist.find(clhash);
            time(&time_now);
            (*ti).second.check_in=(time_now+f_time);
        }
        
        else{
            bzero((char *) &log_msg, sizeof(log_msg));
            log_msg.id_message=LOG_QUEUE_ERR_OUT_SEND;
            log_msg.cli_addr=request.cli_addr;
            log_msg.package=MSG->pkg;
            msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
            sizeof(log_msg)-sizeof(long),IPC_NOWAIT);    
        }
    
    
}
inline void Quimera_server::Guard_report_procedure(Message_queue request){}
inline void Quimera_server::Missing_report_procedure(Message_queue request){}
inline void Quimera_server::Complaint_procedure(Message_queue request){
     
    bzero((char *)&buff, sizeof(buff));
    
    MSG->Confirmation(request.package); 
    buff.cli_addr=request.cli_addr;
    buff.cli_addr.sin_port=htons(QUIMERA_PORT);
    buff.package=MSG->pkg;
   
    
    bzero((char *) &mh, sizeof(mh));
    bzero((char *) &v, sizeof(v));
    mh.msg_name = &buff.cli_addr;
    mh.msg_namelen = sizeof(buff.cli_addr);
    mh.msg_iovlen = 1;
        
    bzero((char *) &envelope, sizeof(envelope));
    qpkg=&buff.package;
    envelope.buff=(void*)qpkg;
    envelope.length=sizeof(*qpkg);
    
    if(Send(sockfd,mh,v,envelope)>0){
        
        bzero((char *) &log_msg, sizeof(log_msg));
        log_msg.id_message=(request.package.header.type_message==GUARD_REPORT) ? LOG_QUEUE_EVNT_OUT_GUARD_C : LOG_QUEUE_EVNT_OUT_MISSING_C;
        log_msg.cli_addr=request.cli_addr;
        log_msg.package=MSG->pkg;
        msgsnd(l_queue_id,(struct msgbuf *)&log_msg,
        sizeof(log_msg)-sizeof(long),IPC_NOWAIT);bzero((char *) &log_msg, sizeof(log_msg));
        
        h_seed=inet_ntoa(request.package.id_client.sin_addr);
        sprintf(this->port,"%d",ntohs(request.package.id_client.sin_port)); 
        h_seed+=port;
        std::cout<<"La semilla:"<<h_seed<<std::endl;
        m_ghash = C_hash(h_seed,std::use_facet<std::collate<char> >(loc));
        std::cout<<"m_ghash:"<<int(m_ghash)<<std::endl;
        mgit=mglist.find(m_ghash);
        
        if(mgit==mglist.end()){
            
            time(&time_now);
            m_gmap.checkin_time=time_now;
            m_gmap.confirmation=true;
            m_gmap.message=request;
            

            mgit=mglist.begin();
            mglist.insert(std::pair<long,msg_map>(m_ghash,m_gmap));

            std::cout<<"El tiempo de introduccion ha sido de:"<<(*mgit).second.checkin_time<<std::endl;
        }
        else{
            if((*mgit).second.message.package.header.type_message!=request.package.header.type_message){
                std::cout<<"Voy a mandar un adoption paper\n";
                
                bzero((char *)&buff, sizeof(buff));
                bzero((char *)&buff_a, sizeof(buff_a));
                
                buff=(request.package.header.type_message==GUARD_REPORT) ? (*mgit).second.message : request;
                buff_a=(request.package.header.type_message==MISSING_REPORT) ? (*mgit).second.message : request;
                
                MSG->Fill_header(domain,quimera_id,buff.package.header.id_server_source);
                
                MSG->Adoption_papers(buff.package.id_client,buff.package.acknowledge_number,
                                     buff.package.sequence_number,buff.package.next_sequence_number,
                                     buff_a.cli_addr,buff_a.package.header.id_server_source);
                                        
                                     
                MSG->pkg.header.n_register=MSG->Get_n_register();
                buff.package=MSG->pkg;
                buff.cli_addr.sin_port=htons(QUIMERA_PORT);
                
                bzero((char *) &mh, sizeof(mh));
                bzero((char *) &v, sizeof(v));
                mh.msg_name = &buff.cli_addr;
                mh.msg_namelen = sizeof(buff.cli_addr);
                mh.msg_iovlen = 1;
        
                bzero((char *) &envelope, sizeof(envelope));
                qpkg=&buff.package;
                envelope.buff=(void*)qpkg;
                envelope.length=sizeof(*qpkg);
                
                 if(Send(sockfd,mh,v,envelope)>0){
                     
                     bzero((char *) &log_msg, sizeof(log_msg));
                     log_msg.id_message=LOG_QUEUE_EVNT_OUT_ADOPTION_P;
                     log_msg.cli_addr=buff.cli_addr;
                     log_msg.package=buff.package;
                     msgsnd(l_queue_id,(struct msgbuf *)&log_msg,
                     sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
                     
                     mglist.erase(m_ghash);
                     
                 }
                
            }
            else{
                bzero((char *) &log_msg, sizeof(log_msg));
                log_msg.id_message=LOG_QUEUE_ERR_IN_REPLY;
                log_msg.cli_addr=buff.cli_addr;
                log_msg.package=buff.package;
                msgsnd(l_queue_id,(struct msgbuf *)&log_msg,
                sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
            }
            
        }
    }
    
}

inline void Quimera_server::Adoption_paper_procedure(Message_queue){}

inline Quimera_client::Quimera_client(const Quimera_client &copy){
    sockfd=copy.sockfd; 
    mh=copy.mh; 
    v[0]=copy.v[0];
    upkr_queue_id=copy.upkr_queue_id;
}
inline Quimera_client::Quimera_client(unsigned char &dom, struct sockaddr_in &iprserv,
                                      unsigned short &intv,unsigned int &bc){
        
    bzero((char *) &mh, sizeof(mh));
    bzero((char *) &v, sizeof(v));
    
    
    domain=dom;
    p_this=this;
    ip_root_serv.sin_family=AF_INET;
    ip_root_serv.sin_addr.s_addr=iprserv.sin_addr.s_addr;
    ip_root_serv.sin_port = htons(QUIMERA_PORT);
    
    interval=intv;
    beacon_number=bc;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    mh.msg_name = &ip_root_serv;
    mh.msg_namelen = sizeof(ip_root_serv);
    mh.msg_iovlen = 1;
    mutex = PTHREAD_MUTEX_INITIALIZER;
    strike=false;
    
    handle = dlopen("libframe.so", RTLD_LAZY);
    create = (Message* (*)())dlsym(handle, "create_t");
    destroy = (void (*)(Message*))dlsym(handle, "destroy_t");
    MSG = (Message*)create();
    qpkg=&MSG->pkg;
    l_queue_id=Message_queue_setup(LOGGER_QUEUE_KEY);
    upkr_queue_id=Message_queue_setup(C_UNPACKER_QUEUE_KEY);
    
    api_queue_id=Message_queue_setup(API_QUEUE_KEY);
    if(upkr_queue_id==EXIT_FAILURE and l_queue_id!=EXIT_FAILURE or upkr_queue_id==EXIT_FAILURE ){
        
        bzero((char *) &log_msg, sizeof(log_msg));
        log_msg.id_message=LOG_QUEUE_ERR_UNPACKER_QCP;
        msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
        sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
        quality_control=false;
    }
    else if(l_queue_id==EXIT_FAILURE)
        quality_control=false;
    else
        quality_control=true;  
    
}


inline Quimera_client::~Quimera_client(){
    
    close(sockfd);
    destroy(MSG);
    pthread_mutex_destroy(&mutex);
}


inline int Quimera_client::Connect(){
    bzero((char *) &envelope, sizeof(envelope));
    bzero((char *) &ref, sizeof(ref));
    qpkg=&MSG->pkg;
    MSG->Membership();
    MSG->pkg.header.n_register=MSG->Get_n_register();
    envelope.buff=(void*)qpkg;
    envelope.length=sizeof(*qpkg);
   
    
    if(Send(sockfd,mh,v,envelope)>0){
        
        bzero((char *) &log_msg, sizeof(log_msg));
        log_msg.id_message=LOG_QUEUE_EVNT_OUT_MEMBERSHIP;
        log_msg.cli_addr=ip_root_serv;
        log_msg.package=MSG->pkg;
        msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
        sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
        
        if(SendRecv(upkr_queue_id,I,this->interval,this->MSG->pkg.header.n_register,ref)==EXIT_SUCCESS){
            
            quimera_id=ref.header.id_server_destination;
            id_root_serv=ref.header.id_server_source;
            f_time=ref.f_time;
            online=true;
            return (EXIT_SUCCESS);   
        }
        else{
            bzero((char *) &log_msg, sizeof(log_msg));
            log_msg.id_message=LOG_QUEUE_ERR_TIMEOUT;
            msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
            sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
            
            online=false;
            return(EXIT_FAILURE);
        }
        
        
    }
    
    else{
        
        bzero((char *) &log_msg, sizeof(log_msg));
        log_msg.id_message=LOG_QUEUE_ERR_OUT_SEND;
        log_msg.cli_addr=ip_root_serv;
        log_msg.package=MSG->pkg;
        msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
        sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
        
        return (EXIT_FAILURE);
    }

}
inline void Quimera_client::Abort(){
    
    bzero((char *) &log_msg, sizeof(log_msg));
    log_msg.id_message=LOG_QUEUE_ERR_TIMEOUT;
    msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
    sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
    
}

inline void Quimera_client::C_success_report(){
    
    bzero((char *) &log_msg, sizeof(log_msg));
    log_msg.id_message=LOG_QUEUE_ENVT_CONNECT;
    msgsnd(l_queue_id, (struct msgbuf *)&log_msg,
    sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
}

inline void* Quimera_client::Keep_intouch(void *args){
    
    Quimera_client *qc=(Quimera_client*)args;
    
    Envlp envelope;
    Package alive_msg;
    
    pthread_mutex_lock(&qc->mutex);
    if(qc->interval>0 and qc->beacon_number>0){
        
        qc->beacon_count=qc->beacon_number;
    }
    
    else{
        qc->interval=DEFAULT_INTVL;
        qc->beacon_count=DEFAULT_BN;
    }
  
    pthread_mutex_unlock(&qc->mutex);
    usleep(DEFAULT_WAIT);
    
    while (qc->beacon_count>0 and !qc->interrupt){
        
     
        bzero((char *) &envelope, sizeof(envelope));
     
        qc->MSG->Confirmation(qc->ref);
        qc->MSG->pkg.header.n_register=qc->MSG->Get_n_register();
        qc->tic=qc->iclist.begin();
        qc->iclist.insert(std::pair<unsigned int,bool>(qc->MSG->pkg.header.n_register,true));
        
        bzero((char *) &alive_msg, sizeof(alive_msg));
        alive_msg=qc->MSG->pkg;   
        
        envelope.buff=(void*)&alive_msg;
        envelope.length=sizeof(alive_msg);
       
        qc->strike=true;
        if(qc->Send(qc->sockfd, qc->mh,qc->v, envelope)>0){
            
            bzero((char *) &qc->log_msg, sizeof(qc->log_msg));
            qc->log_msg.id_message=LOG_QUEUE_EVNT_OUT_INVESTMENT_C;
            qc->log_msg.cli_addr=qc->ip_root_serv;
            qc->log_msg.package=alive_msg;
            msgsnd(qc->l_queue_id, (struct msgbuf *)&qc->log_msg,
            sizeof(qc->log_msg)-sizeof(long),IPC_NOWAIT);
            
            
            usleep(U_SECONDS*qc->interval);
            
            if(qc->strike)
                qc->beacon_count-=1;
            
            if(qc->beacon_count==0){
                qc->interrupt=true;
                std::cout<<"Interruption\n";
            }
        }
        else{
            bzero((char *) &qc->log_msg, sizeof(qc->log_msg));
            qc->log_msg.id_message=LOG_QUEUE_ERR_OUT_SEND;
            msgsnd(qc->l_queue_id, (struct msgbuf *)&qc->log_msg,
            sizeof(qc->log_msg)-sizeof(long),IPC_NOWAIT);
        }
    }
    
    if(qc->interrupt)
        qc->Abort();
    
    qc->online=false;
}

inline int Quimera_client::Listen(){
    
    
    while(interrupt==false){ 
       
        bzero((char *) &buff, sizeof(buff));
        msgrcv(upkr_queue_id,(struct msgbuf *)&buff,sizeof(buff)-sizeof(long),0,IPC_NOWAIT);
        switch(buff.package.header.type_message){
       
            case INVESTMENT_CONFIRMATION:{
                Investment_procedure(buff);
                std::cout<<"He regresado\n";
                break;
            }
            
            case GUARD_REPORT:{
                Guard_report_procedure(buff);
                break;
            }
            case MISSING_REPORT:{
                Missing_report_procedure(buff);
                break;
            }
            case ADOPTION_PAPERS:{
                Adoption_paper_procedure(buff);
                break;
            }
        }
        if(buff.id_message==CLR){ 
         interrupt=true;
        }
        usleep(DEFAULT_WAIT);
    }
    msgctl (upkr_queue_id, IPC_RMID, (struct msqid_ds *)NULL);
    return(EXIT_FAILURE);

}

Thd_args::Thd_args(unsigned short &i, int &bn, Package &rf, Quimera_client &qc) : 
intvl(i), beacon_number(bn), ref(rf), x(qc) {} 

inline void Quimera_client::Membership_procedure(Message_queue request){}
inline void Quimera_client::Investment_procedure(Message_queue request){

    tic=iclist.find(request.package.header.n_register);
    if(tic!=iclist.end()){
        
        strike=false;
        beacon_count=beacon_number;
        iclist.erase(request.package.header.n_register);
    }  

}
inline void Quimera_client::Guard_report_procedure(Message_queue request){
    
    std::cout<<"En guard_procedure\n";
    bzero((char *) &buff, sizeof(buff));
    
    MSG->Fill_header(domain,quimera_id, id_root_serv);
    MSG->pkg.header.n_register=MSG->Get_n_register();
    MSG->Guard_report(request.package.id_client);
 
    bzero((char *) &envelope, sizeof(envelope));
    qpkg=&MSG->pkg;
    envelope.buff=(void*)qpkg;
    envelope.length=sizeof(*qpkg);
    
    if(Send(sockfd,mh,v,envelope)>0){
            bzero((char *) &log_msg, sizeof(log_msg));
            log_msg.id_message=LOG_QUEUE_EVNT_OUT_GUARD;
            log_msg.cli_addr=ip_root_serv;
            log_msg.package=MSG->pkg;
            msgsnd(l_queue_id,(struct msgbuf *)&log_msg,
            sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
            std::cout<<"Lo envie\n";
    }
    
     
    else{
        bzero((char *) &log_msg, sizeof(log_msg));
        log_msg.id_message=LOG_QUEUE_ERR_OUT_SEND;
        log_msg.cli_addr=request.cli_addr;
        log_msg.package=MSG->pkg;
        msgsnd(l_queue_id, (struct msgbuf *)&this->log_msg,
        sizeof(log_msg)-sizeof(long),IPC_NOWAIT);    
    }
    
}

inline void Quimera_client::Missing_report_procedure(Message_queue request){
    
    std::cout<<"En Missing_procedure\n";
    bzero((char *) &buff, sizeof(buff));
    
    MSG->Fill_header(domain,quimera_id, id_root_serv);
    MSG->pkg.header.n_register=MSG->Get_n_register();
    MSG->Missing_report(request.package.id_client,request.package.acknowledge_number,
         request.package.sequence_number,request.package.next_sequence_number);
    
    
    bzero((char *)&envelope, sizeof(envelope));
    qpkg=&MSG->pkg;
    envelope.buff=(void*)qpkg;
    envelope.length=sizeof(*qpkg);
    
    if(Send(sockfd,mh,v,envelope)>0){
            bzero((char *) &log_msg, sizeof(log_msg));
            log_msg.id_message=LOG_QUEUE_EVNT_OUT_MISSING;
            log_msg.cli_addr=ip_root_serv;
            log_msg.package=MSG->pkg;
            msgsnd(l_queue_id,(struct msgbuf *)&log_msg,
            sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
            std::cout<<"Lo envie\n";
    }
    
     
    else{
        bzero((char *) &log_msg, sizeof(log_msg));
        log_msg.id_message=LOG_QUEUE_ERR_OUT_SEND;
        log_msg.cli_addr=request.cli_addr;
        log_msg.package=MSG->pkg;
        msgsnd(l_queue_id, (struct msgbuf *)&this->log_msg,
        sizeof(log_msg)-sizeof(long),IPC_NOWAIT);    
    }
    
}

inline void Quimera_client::Complaint_procedure(Message_queue request){}
inline void Quimera_client::Adoption_paper_procedure(Message_queue){
    
}

/*
int main(int argc, char **argv){
    
    unsigned char domain;
    unsigned short f_time;
    Q_pool pool;
    Q_density q_dent;
    void *v;
    pthread_t thd;
    bzero((char *) &pool, sizeof(pool));
    bzero((char *) &q_dent, sizeof(q_dent));
    
    domain=unsigned(char(atoi(argv[2])));
    f_time=unsigned(short(atoi(argv[3])));
    pool.first=unsigned(atoi(argv[4]));
    pool.last=unsigned(atoi(argv[5]));
    q_dent.cycle=unsigned(short(atoi(argv[6])));
    q_dent.switchpop=unsigned(short(atoi(argv[7])));
    
    Quimera_server Q_S(domain,pool,f_time,q_dent);
    
    Q_S.Listen();
    
    return (EXIT_FAILURE);
}
int main(int argc, char **argv){
    
    unsigned char domain;
    unsigned short interval;
    int beacon_number;
    struct sockaddr_in iprserv;
    Package ref;
    
    void *args;
    pthread_t init_contact; 
    
    bzero((char *) &iprserv, sizeof(iprserv));
    domain=unsigned(char(atoi(argv[2])));
    iprserv.sin_addr.s_addr=inet_addr(argv[3]);
    interval = atoi(argv[4]);
    beacon_number = atoi(argv[5]);
    
    
    Quimera_client QC(domain,iprserv);

    if(QC.Connect()==EXIT_SUCCESS and QC.quality_control){
       
        Thd_args t_args(interval,beacon_number,ref,QC);
        args=(void*)&t_args;
   
    
        if(pthread_create( &init_contact, NULL, Keep_intouch, args)==0){
            
            while(QC.online){
            usleep(100000);
            std::cout<<"Todavia en linea\n";
            }
        
        }
    
        else{
            bzero((char *) &QC.log_msg, sizeof(QC.log_msg));
            QC.log_msg.id_message=LOG_QUEUE_ERR_TOUCH;
            msgsnd(QC.l_queue_id, (struct msgbuf *)&QC.log_msg,
            sizeof(QC.log_msg)-sizeof(long),IPC_NOWAIT);
            return (EXIT_FAILURE);
        }
    }
    return(EXIT_FAILURE);
}*/


